#include <Core/logger/logger.h>
#include "sslbase.h"
#include "common.h"
#include <QtNetwork/QSslCipher>
#include <QtNetwork/QNetworkProxy>

//#include <protocol/message.h>

namespace Opl
{
    namespace Helpers
    {
        namespace Ssl
        {

            using Opl::Core::EntityObject;
            //using namespace Opl::Protocol;

            class SslBasePrivate
            {
                public:
                    SslBasePrivate(): status(Disconnected), socket(0)
                    {}
                    ~SslBasePrivate() {}
                    Opl::Helpers::ConnectionStatus status;
                    QSslSocket* socket;
                    Opl::Helpers::ConnectionStatus updateState()const
                    {
                        LI.Trace() << "Update Socket state";
                        if (status == Connecting)
                        {
                            return status;
                        }

                        bool unconnected = !socket || socket->state() == QAbstractSocket::UnconnectedState;

                        if (unconnected)
                        {
                            return Disconnected;
                        }

                        bool connected = socket && socket->state() == QAbstractSocket::ConnectedState;

                        if (connected)
                        {
                            return Connected;
                        }

                        return status;
                    }
            };


            SslBase::SslBase(QSslSocket* socket) :
                Opl::Core::EntityObject(0), O_PRIVATE_CREATE(SslBase)
            {
                O_D(SslBase);
                d->socket = socket;
                //LI.Debug() << "Create SslBase";
            }

            SslBase::~SslBase()
            {
                O_PRIVATE_DELETE;
            }

            void SslBase::setSocket(QSslSocket* s)
            {
                O_D(SslBase);
                Q_ASSERT(s);
                d->socket = s;
            }

            QSslSocket* SslBase::socket()const
            {
                O_C(SslBase);
                return d->socket;
            }

            void SslBase::setStatusInternal(Opl::Helpers::ConnectionStatus s)
            {
                O_D(SslBase);
                d->status = s;
            }

            Opl::Helpers::ConnectionStatus SslBase::status()const
            {
                O_C(SslBase);
                return d->updateState();
            }

            void SslBase::sslErrors(const QList<QSslError>& errors)
            {
                O_D(SslBase);
                foreach(QSslError er, errors)
                {
                    LI.Error() << "SslError:" << er.errorString();
                }

                d->socket->ignoreSslErrors();
            }

            void SslBase::modeChanged(QSslSocket::SslMode mode)
            {
                LI.Info() << "Ssl modeChanged:" << mode;
            }

            void SslBase::peerVerifyError(const QSslError& error)
            {
                LI.Error() << "SslError-peerVerifyError:" << error.errorString();
            }

            void SslBase::socketConnected()
            {
                O_D(SslBase);
                LI.Info("socketConnected");
                d->status = Connected;
                d->updateState();
                emit connected();
            }

            void SslBase::socketDisconnected()
            {
                O_D(SslBase);
                LI.Info("socketDisconnected");
                d->status = Disconnected;
                d->updateState();
                emit disconnected();
            }

            void SslBase::error(QAbstractSocket::SocketError socketError)
            {
                LI.Info() << "SocketError:" << socketError << " " << socket()->errorString();
                emit error();
            }

            void SslBase::hostFound()
            {
                LI.Debug("hostFound");
            }

            void SslBase::proxyAuthenticationRequired (const QNetworkProxy& proxy, QAuthenticator* authenticator)
            {
                LI.Trace() << "proxyAuthenticationRequired:" << proxy.hostName();
            }

            void SslBase::stateChanged(QAbstractSocket::SocketState socketState)
            {
                LI.Trace() << "SocketState:" << socketState;
                O_D(SslBase);
                d->updateState();

                if (socketState == QAbstractSocket::UnconnectedState)
                {
                    LI.Info("UnconnectedState");
                }
            }

            void SslBase::ready()
            {
                O_D(SslBase);

                if (!d->socket) {
                    LI.Error("encrypted ready comand but socket is null (can be already disconnected)");
                    return;
                }

                QSslCipher ciph = d->socket->sessionCipher();
                QString cipher = QString("%1, %2 (%3/%4)").arg(ciph.authenticationMethod()).arg(ciph.name()).arg(ciph.usedBits()).arg(ciph.supportedBits());
                LI.Info() << "SslBase::Reconnect:ready() cipher:" << cipher;
                d->status = Connected;
                emit connected();
            }

            void SslBase::readyCommand()
            {
                O_D(SslBase);
                QSslSocket *client = (QSslSocket*)sender();
                QByteArray arr = client->readAll();

                //QDataStream sstream(arr);
                //Message msg;
                //sstream >> msg;

                emit output(arr);
            }

            void SslBase::Send(const QString& data)
            {
                O_D(SslBase);
                QDataStream datastr(d->socket);
                //Message msg;//TODO: = Message::createCommand(data);
                datastr << data;//msg;
                d->socket->flush();
            }

            void SslBase::stop()
            {
                O_D(SslBase);
                d->socket->close();
                d->updateState();
                d->socket->deleteLater();
                d->socket = 0;
            }

            void SslBase::start()
            {
                O_D(SslBase);
                d->socket = new QSslSocket(this);
                LI.Info("SslBase::start");
                connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)));
                connect(d->socket, SIGNAL(encrypted()), this, SLOT(ready()));
                connect(d->socket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(sslErrors(const QList<QSslError>&)));
                connect(d->socket, SIGNAL(readyRead()), this, SLOT(readyCommand()));
                connect(d->socket, SIGNAL(hostFound()), this, SLOT(hostFound()));
                connect(d->socket, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(peerVerifyError(const QSslError&)));
                connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
                connect(d->socket, SIGNAL(connected()), this, SLOT(socketConnected()));
                connect(d->socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
                connect(d->socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(modeChanged(QSslSocket::SslMode)));
                connect(d->socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), this, SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));

                foreach(QSslCipher c, d->socket->defaultCiphers())
                {
                    LI.Trace() << "Ciphers:" << c.name();
                }

                d->updateState();
            }

        }
    }
}
